package com.example.java.base.reflect;

import org.junit.Test;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;

/**
 * ReflectDemo02:反射获取类的实例,并操作
 *
 * @author zhangxiaoxiang
 * @date 2020/11/3
 */
public class Reflect2Test {
    /**
     * 为了方便,这里定义一个方法获取反射,方便后面测试,这里使用的方式一为示例
     *
     * @return 获取到的反射对象
     */
    public static Class<?> getClassTest() {
        Class<?> perClazz = null;
        try {
            perClazz = Class.forName("com.example.java.base.reflect.Person");
            return perClazz;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取对象的实例，并操作对象
     *
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public static void demo01() throws IllegalAccessException, InstantiationException, NoSuchFieldException,
            NoSuchMethodException, InvocationTargetException, IOException {
        Class<?> perClazz = getClassTest();
        Person per = (Person) Objects.requireNonNull(perClazz).newInstance();
        per.setId(1);
        per.setName("zs");
        per.setAge(23);
        per.setDesc("我是描述属性");
        System.out.println("===============================为对象赋值============================================");
        System.out.println(per.toString());
        System.out.println("===============================操作属性============================================");
        Field idField = perClazz.getDeclaredField("id");
        //访问的是private修饰的id，但是private是私有
        //修改属性的访问权限  使用反射时，如果 因为访问修饰符限制造成异常，可以通过  Field/Method/Constructor.setAccessible(true)
        idField.setAccessible(true);
        //per.setId(1);
        idField.set(per, 2);
        System.out.println("修改私有属性后:"+per.toString());
        Method method = perClazz.getDeclaredMethod("privateMethodTest", null);
        method.setAccessible(true);
        //方法的调用：invoke()
        method.invoke(per, null);
        //per.say(xxx);
        Method method2 = perClazz.getDeclaredMethod("privateMethodTest", String.class);
        method2.setAccessible(true);
        method2.invoke(per, "我是pram");
        System.out.println("===============================操作构造方法============================================");
        //		Constructor<?>[] constructors = perClazz.getConstructors() ;//公共
        //		for(Constructor constructor:constructors) {
        //			System.out.println(constructor);
        //		}

        //		Constructor<?>[] constructors = perClazz.getDeclaredConstructors() ;//本类
        //		for(Constructor constructor:constructors) {
        //			System.out.println(constructor);
        //		}

        //获取指定的构造方法
        //在反射中，根据类型 获取方法时：基本类型（int、char...）和包装类(Integer,Character)是不同的类型
        // Constructor<?> constructor = perClazz.getConstructor(Integer.class);
        Constructor<?> constructor = perClazz.getConstructor(int.class);
        System.out.println("获取指定的构造方法===>"+constructor);

        Constructor<?> constructor2 = perClazz.getDeclaredConstructor(String.class);
        System.out.println("获取指定的构造方法===>"+constructor2);
        //Person per = new Person() ;


        //根据获取的private构造方法，获取对象实例
        constructor2.setAccessible(true);
        Person per3 = (Person) constructor2.newInstance("zs");
        System.out.println("获取带有String的构造方法" + per3);
        //因为constructor是 有参构造方法(Integer还是int这里构造的就是什么值)
        Person instance2 = (Person) constructor.newInstance(23);
        System.out.println("获取带有Integer的构造方法" +instance2);

        Constructor<?> constructor3 = perClazz.getConstructor();
        //因为constructor3是 无参构造方法，因此不需要传值
        Person instance = (Person) constructor3.newInstance();
        System.out.println("无参构造"+instance);
        System.out.println("===============================动态加载 类名 和方法===============================");
        Properties prop = new Properties();
        prop.load(new FileReader("src/main/resources/class.txt"));
        String classname = prop.getProperty("classname");
        String methodname = prop.getProperty("methodname");
        Class<?> perClazz1 = null;
        try {
            perClazz1 = Class.forName(classname);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        method = perClazz1.getMethod(methodname);
        System.out.println("===>反射读取配置文件并调用");
        method.invoke(perClazz1.newInstance());
        System.out.println("===============================反射可以 越过泛型检查===============================");
        System.out.println("虽然可以通过反射 访问private等访问修饰符不允许访问的属性/方法，也可以忽略掉泛型的约束;" +
                "但实际开发 不建议这样使用，因此可能造成程序的混乱。");
        ArrayList<Integer> list = new ArrayList<>();
        list.add(123);
        list.add(3);
        list.add(2);
//		list.add("zs") ;

        Class<?> listClazz = list.getClass();
         method = listClazz.getMethod("add", Object.class);
        method.invoke(list, "zs...");

        System.out.println("反射可以 越过泛型检查===>"+list);

        System.out.println("===============================使用工具类复制===============================");
        Person person = new Person();
        PropertyUtil.setProperty(person, "name", "长草颜团子");
        PropertyUtil.setProperty(person, "age", 18);
        Student stu = new Student();
        PropertyUtil.setProperty(stu, "score", 98);

        System.out.println(person.toString());
        System.out.println(stu.getScore());
    }
    @Test
    public void test2() throws InstantiationException, IllegalAccessException, NoSuchMethodException,
            NoSuchFieldException, InvocationTargetException, IOException {
        System.out.println("************************反射获取类的实例,并操作************************");
        demo01();
    }


}